from datetime import datetime
from decimal import Decimal
from random import randint
from rest_framework.fields import SerializerMethodField
from rest_framework.serializers import ModelSerializer
from rest_framework import serializers
from django.db import transaction
from shopping.models import *
from user.serializers import AddressSerializer


class ClassificationSerializer(ModelSerializer):
    class Meta:
        model = Classification
        # exclude = ['is_delete']
        fields = ['id', 'name', 'parent']
        extra_kwargs = {
            'parent': {'write_only': True}
        }


class ParentClassificationSerializer(ModelSerializer):
    classification_set = ClassificationSerializer(many=True, read_only=True)

    class Meta:
        model = Classification
        # exclude = ['parent']
        fields = ['id', 'name', 'parent', 'classification_set']
        extra_kwargs = {
            'parent': {'write_only': True}
        }


class CommodityImgSerializer(ModelSerializer):
    class Meta:
        model = CommodityImg
        # exclude = ['is_delete']
        fields = ['src']
        # extra_kwargs = {
        #     'parent': {'write_only': True}
        # }


class CommoditySerializer(ModelSerializer):
    classification1_name = serializers.CharField(source='classification1.name', read_only=True)
    classification2_name = serializers.CharField(source='classification2.name', read_only=True)
    commodityimg_set = CommodityImgSerializer(many=True, read_only=True)

    class Meta:
        model = Commodity
        exclude = ['is_delete']
        # fields = ['id', 'name', 'parent', 'classification_set']
        # extra_kwargs = {
        #     'parent': {'write_only': True}
        # }


class ShoppingCartSerializer(ModelSerializer):
    # 这个方法可以调用这个序列化器上的某个方法返回的值。比如这里可以获取get_commodityDetail返回的值，这个变量是购物车商品详情，前端需要
    commodityDetail = SerializerMethodField()

    class Meta:
        model = ShoppingCart
        # exclude = ['is_delete']
        fields = '__all__'
        # extra_kwargs = {
        #     'parent': {'write_only': True}
        # }

    def get_commodityDetail(self, cart):  # cart=ShoppingCart()，模型类的实例化对象，cart作为变量名，可以随便改
        return CommoditySerializer(cart.commodity).data

    # 校验购物车商品数量，重新定义校验方法
    def validate(self, attrs):
        if attrs['number'] <= 0 or attrs['number'] > attrs['commodity'].stock:
            raise serializers.ValidationError('number error')
        return attrs


class OrderSerializer(ModelSerializer):
    cart = serializers.ListField(write_only=True)  # 当需要接收一个list的时候使用，传入list中必须是int类型，数据来源于前端cart字段
    addressDetail = serializers.SerializerMethodField()

    class Meta:
        model = Order
        # exclude = ['is_delete']
        fields = '__all__'
        read_only_fields = ['order_id', 'total_count', 'total_amount', 'status', 'user']
        # extra_kwargs = {
        #     'parent': {'write_only': True}
        # }

    def create(self, validated_data):
        """
        根据所需字段，设置和获取所需字段
        1 生成订单编号
        2 创建订单信息，事务控制（保证订单数量在有效范围内）
            2.1 设置保存点
            2.2 创建订单信息
            2.3 获取购物车中被勾选的商品
                2.3.1 判断是否超出库存，超出回滚
                2.3.2 库存操作：商品库存减少，销量增加
                2.3.3 将购物车中要结算的商品保存到订单商品表中
                2.3.4 得到总数量和总价格
        3 修改订单状态
        4 清除购物车已结算商品
        :param validated_data: 已经校验过的数据
        :return:
        """
        # 1 生成订单编号（唯一性+时间+当前结算用户id+随机数）
        user = self.context['request'].user
        order_id = datetime.now().strftime('%Y%m%d%H%M%s') + '%06d%02d' % (user.id, randint(1, 100))
        pay_method = validated_data.get('pay_method')
        address = validated_data.get('address')
        # 2 创建订单信息，事务控制（保证订单数量在有效范围内）
        with transaction.atomic():
            # 2.1 设置保存点
            save_point = transaction.savepoint()
            # 2.2 创建订单信息
            order = Order.objects.create(
                order_id=order_id,
                total_count=0,  # 设置初始值，后面再修改
                total_amount=Decimal('0.00'),  # 设置初始值，后面再修改
                status=1 if pay_method == 1 else 2,
                pay_method=pay_method,
                address=address,
                user=user,
            )
            # 2.3 获取购物车中被勾选的商品
            try:
                carts = validated_data.get('cart')
                for cart_id in carts:
                    while True:
                        cart = ShoppingCart.objects.get(id=cart_id)
                        commodity = cart.commodity
                        # 2.3.1 判断是否超出库存，超出回滚
                        # 目的：乐观锁修改时做检查使用
                        origin_stock = commodity.stock
                        origin_sales = commodity.sales
                        # if cart.number > commodity.stock:
                        if cart.number > origin_stock:
                            raise serializers.ValidationError('库存不足')
                        # 2.3.2 库存操作：商品库存减少，销量增加
                        # commodity.stock -= cart.number
                        # commodity.sales += cart.number
                        # commodity.save()
                        new_stock = origin_stock - cart.number
                        new_sales = origin_sales + cart.number
                        # 乐观锁，update会返回影响的数据的条数
                        res = Commodity.objects.filter(id=commodity.id, stock=origin_stock).update(stock=new_stock,
                                                                                                   sales=new_sales)
                        if not res:  # 购买不成功
                            continue  # 跳出当前循环，进入下一次循环
                        # 2.3.3 将购物车中要结算的商品保存到订单商品表中
                        OrderGoods.objects.create(
                            number=cart.number,
                            price=commodity.price,
                            order=order,
                            commodity=commodity,
                        )
                        # 2.3.4 得到总数量和总价格
                        order.total_count += cart.number
                        order.total_amount += (commodity.price * cart.number)
                        break  # 购买成功，跳出循环
                # 3 修改订单信息
                order.save()
            except Exception as e:
                transaction.savepoint_rollback(save_point)  # 有异常回滚到保存点
                raise serializers.ValidationError(e)
            else:
                transaction.savepoint_commit(save_point)  # 没有异常，提交到保存点
        # 4 清除购物车已结算商品
        ShoppingCart.objects.filter(id__in=carts).delete()
        return order

    def get_addressDetail(self, order):  # order=Order()，模型类的实例化对象，order作为变量名，可以随便改
        return AddressSerializer(order.address).data
